home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / OutOfPhase1.01Source / OutOfPhase Folder / Level 0 Macintosh 07Aug94 / DirStuff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-01  |  24.6 KB  |  876 lines  |  [TEXT/KAHL]

  1. /* DirStuff.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Debug.h"
  21. #include "Audit.h"
  22. #include "Definitions.h"
  23.  
  24. #pragma options(pack_enums)
  25. #include <Files.h>
  26. #include <Errors.h>
  27. #include <Aliases.h>
  28. #include <Script.h>
  29. #pragma options(!pack_enums)
  30.  
  31. #include "DirStuff.h"
  32. #include "Memory.h"
  33. #include "Files.h"
  34.  
  35.  
  36. #define MaxFilenameSize (256)
  37.  
  38. typedef struct DirEntry
  39.     {
  40.         struct DirEntry*        Next;
  41.         FileSpec*                        WhereIsIt;
  42.         DirFileTypes                ItemType;
  43.     } DirEntry;
  44.  
  45. struct DirectoryRec
  46.     {
  47.         DirEntry*                        List;
  48.     };
  49.  
  50.  
  51. #define AUDITFILESPEC(x) APRINT((" "#x": v=%s,par=%l,%p",((FSSpec*)&(x))->vRefNum,\
  52.                     ((FSSpec*)&(x))->parID,((FSSpec*)&(x))->name))
  53.  
  54.  
  55. /* local routine to dereference a folder.  You supply the folder's location */
  56. /* in TheFolder (name + parent) and it returns the ID of the folder in *FolderID */
  57. /* (so you can use it as the parent of something inside the folder) */
  58. static OSErr            FDerefFolder(FSSpec* TheFolder, long* FolderID)
  59.     {
  60.         CInfoPBRec        MyPB;
  61.         OSErr                    Error;
  62.         FSSpec                Copy;
  63.         Boolean                TargetWasFolder;
  64.         Boolean                TargetWasAlias;
  65.  
  66.         APRINT(("+FDerefFolder v=%s,par=%l,%p",TheFolder->vRefNum,TheFolder->parID,
  67.             TheFolder->name));
  68.         Copy = *TheFolder;
  69.         AUDITFILESPEC(Copy);
  70.         Error = ResolveAliasFile(&Copy,False/*oneonly*/,&TargetWasFolder,&TargetWasAlias);
  71.         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",(short)Error,
  72.             (short)TargetWasFolder,(short)TargetWasAlias));
  73.         if (Error == fnfErr)
  74.             {
  75.                 /* assume it's a volume. */
  76.                 /* return the volume's root directory number (no indirection) */
  77.                 *FolderID = Copy.parID;
  78.                 APRINT(("-FDerefFolder dirID=%l",*FolderID));
  79.                 return noErr;
  80.             }
  81.         MyPB.dirInfo.ioCompletion = NIL;
  82.         MyPB.dirInfo.ioVRefNum = Copy.vRefNum;
  83.         MyPB.dirInfo.ioNamePtr = Copy.name;
  84.         MyPB.dirInfo.ioFDirIndex = 0;
  85.         MyPB.dirInfo.ioDrDirID = Copy.parID;
  86.         Error = PBGetCatInfo(&MyPB,False);
  87.         if (Error == noErr)
  88.             {
  89.                 Error = MyPB.dirInfo.ioResult;
  90.             }
  91.         if (Error != noErr)
  92.             {
  93.             }
  94.          else
  95.             {
  96.                 if ((MyPB.dirInfo.ioFlAttrib & 16) == 0)
  97.                     {
  98.                         /* if it's not a directory, then we return error */
  99.                         Error = dirNFErr;
  100.                     }
  101.                  else
  102.                     {
  103.                         *FolderID = MyPB.dirInfo.ioDrDirID;
  104.                         APRINT(("-FDerefFolder dirID=%l",*FolderID));
  105.                     }
  106.             }
  107.         ERROR(Error != noErr,APRINT(("-FDerefFolder failed %s",Error)));
  108.         return Error;
  109.     }
  110.  
  111.  
  112. /* read the first level list of items in the specified directory.  NIL specifies */
  113. /* a root directory.  If NIL is returned, then the operation could not be completed */
  114. DirectoryRec*            ReadDirectory(FileSpec* Directory)
  115.     {
  116.         DirectoryRec*            Dir;
  117.         DirEntry*                    Tail;
  118.  
  119.         if (Directory != NIL)
  120.             {
  121.                 CheckPtrExistence(Directory);
  122.                 ValidateFileSpec(Directory);
  123.                 APRINT(("+ReadDirectory v=%s,par=%l,%p",((FSSpec*)Directory)->vRefNum,
  124.                     ((FSSpec*)Directory)->parID,((FSSpec*)Directory)->name));
  125.             }
  126.          else
  127.             {
  128.                 APRINT(("+ReadDirectory (root)"));
  129.             }
  130.         Dir = (DirectoryRec*)AllocPtrCanFail(sizeof(DirectoryRec),"DirectoryRec");
  131.         if (Dir == NIL)
  132.             {
  133.                 /* fault tolerance is most studly... */
  134.                 APRINT(("-ReadDirectory failed"));
  135.                 return NIL;
  136.             }
  137.         Tail = NIL;
  138.         Dir->List = NIL;
  139.         if ((NIL != Directory)
  140.             || ((((FSSpec*)Directory)->name[0] == 0)
  141.             && (((FSSpec*)Directory)->vRefNum == 0)
  142.             && (((FSSpec*)Directory)->parID == 0)))
  143.             {
  144.                 CInfoPBRec            CInfo;
  145.                 OSErr                        Error;
  146.                 short                        Index;
  147.                 Str255                    FileName;
  148.                 MyBoolean                EnergizerBunny;
  149.                 short                        VolumeRefNum;
  150.                 long                        DirectoryID;
  151.  
  152.                 /* read through this directory and build a list of contained items */
  153.                 VolumeRefNum = ((FSSpec*)Directory)->vRefNum;
  154.                 if (noErr != FDerefFolder((FSSpec*)Directory,&DirectoryID))
  155.                     {
  156.                         /* oops, we weren't given a directory to look at. */
  157.                         ReleasePtr((char*)Dir);
  158.                         APRINT(("-ReadDirectory failed"));
  159.                         return NIL;
  160.                     }
  161.                 Index = 0;
  162.                 EnergizerBunny = True;
  163.                 while (EnergizerBunny)
  164.                     {
  165.                         Index += 1; /* index started at 0 up there, so we increment first */
  166.                         CInfo.hFileInfo.ioVRefNum = VolumeRefNum;
  167.                         CInfo.hFileInfo.ioNamePtr = FileName;
  168.                         CInfo.hFileInfo.ioDirID = DirectoryID;
  169.                         CInfo.hFileInfo.ioFDirIndex = Index;
  170.                         Error = PBGetCatInfo(&CInfo,False/*synchronously*/);
  171.                         if (Error == noErr)
  172.                             {
  173.                                 DirEntry*                New;
  174.                                 FSSpec*                    Where;
  175.  
  176.                                 APRINT((" ReadDirectory filescan: '%p'",FileName));
  177.                                 /* allocate a new record to hold the directory */
  178.                                 New = (DirEntry*)AllocPtrCanFail(sizeof(DirEntry),"DirEntry");
  179.                                 if (New == NIL)
  180.                                     {
  181.                                         /* this operation is legal because the directory structure is */
  182.                                         /* always consistent at all times. */
  183.                                      FailurePoint:
  184.                                         DisposeDirectory(Dir);
  185.                                         APRINT(("-ReadDirectory failed"));
  186.                                         return NIL;
  187.                                     }
  188.                                 /* make a file specification that references the item */
  189.                                 Where = (FSSpec*)AllocPtrCanFail(sizeof(FSSpec),"DirFSSpec");
  190.                                 if (Where == NIL)
  191.                                     {
  192.                                      WhereFailurePoint:
  193.                                         ReleasePtr((char*)New);
  194.                                         goto FailurePoint;
  195.                                     }
  196.                                 EXECUTE(if (!Eep_RegisterFileSpec((FileSpec*)Where))
  197.                                     {ReleasePtr((char*)Where); goto WhereFailurePoint;})
  198.                                 Error = FSMakeFSSpec(VolumeRefNum,DirectoryID,FileName,Where);
  199.                                 if (Error != noErr)
  200.                                     {
  201.                                         APRINT((" ReadDirectory filescan:  Error %s making FSSpec",Error));
  202.                                         ReleasePtr((char*)New);
  203.                                         DisposeFileSpec((FileSpec*)Where);
  204.                                         goto TrySomeMore;
  205.                                     }
  206.                                 ERROR(((FSSpec*)Where)->name[0] == 0,PRERR(AllowResume,
  207.                                     "ReadDirectory:  FSSpec has empty filename"));
  208.                                 if ((CInfo.hFileInfo.ioFlAttrib & 16/*majiknumber!*/) != 0)
  209.                                     {
  210.                                         /* it's a directory */
  211.                                         APRINT((" ReadDirectory filescan: Directory"));
  212.                                         New->ItemType = eDirectory;
  213.                                     }
  214.                                  else
  215.                                     {
  216.                                         Boolean                    TargetWasFolder;
  217.                                         Boolean                    TargetWasAlias;
  218.                                         FSSpec                    WhereCopy;
  219.  
  220.                                         /* it's a file, but is it an alias? */
  221.                                         WhereCopy = *Where; /* we don't want to keep the changes! */
  222.                                         AUDITFILESPEC(WhereCopy);
  223.                                         Error = ResolveAliasFile(&WhereCopy,False/*stopafterfirst*/,
  224.                                             &TargetWasFolder,&TargetWasAlias);
  225.                                         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",
  226.                                             (short)Error,(short)TargetWasFolder,(short)TargetWasAlias));
  227.                                         if (TargetWasAlias && (Error == noErr))
  228.                                             {
  229.                                                 APRINT((" ReadDirectory filescan: Symbolic Link"));
  230.                                                 New->ItemType = eSymbolicLink;
  231.                                             }
  232.                                         else
  233.                                             {
  234.                                                 APRINT((" ReadDirectory filescan: File"));
  235.                                                 New->ItemType = eFile;
  236.                                             }
  237.                                     }
  238.                                 New->WhereIsIt = (FileSpec*)Where;
  239.                                 New->Next = NIL;
  240.                                 if (Tail != NIL)
  241.                                     {
  242.                                         Tail->Next = New;
  243.                                     }
  244.                                  else
  245.                                     {
  246.                                         Dir->List = New;
  247.                                     }
  248.                                 Tail = New;
  249.                              TrySomeMore:
  250.                                 ;/* we jump here after weird "Desktop DB" stuff */
  251.                             }
  252.                         else /* Error != noErr */
  253.                             {
  254.                                 /* error means there are no more items in the directory */
  255.                                 EnergizerBunny = False;
  256.                             }
  257.                     }
  258.             }
  259.          else
  260.             {
  261.                 OSErr                        Error;
  262.                 short                        Index;
  263.                 Str255                    DiskName;
  264.                 MyBoolean                EnergizerBunny;
  265.                 HParamBlockRec    Params;
  266.  
  267.                 /* root directory.  We fake up a directory containing all of the */
  268.                 /* mounted volumes, kind of like under UNIX, so that it looks like */
  269.                 /* there is only one huge file system. */
  270.                 EnergizerBunny = True;
  271.                 Index = 0;
  272.                 while (EnergizerBunny)
  273.                     {
  274.                         Index += 1; /* index started at 0 up there, so we increment first */
  275.                         Params.volumeParam.ioNamePtr = DiskName;
  276.                         Params.volumeParam.ioVRefNum = 0;  /* says "use the index" */
  277.                         Params.volumeParam.ioVolIndex = Index;
  278.                         Error = PBHGetVInfo(&Params,False/*synchronous*/);
  279.                         if (Error == noErr)
  280.                             {
  281.                                 DirEntry*            New;
  282.                                 FSSpec*                Where;
  283.  
  284.                                 /* add this "directory" to the list */
  285.                                 APRINT((" ReadDirectory volscan: %p",DiskName));
  286.                                 New = (DirEntry*)AllocPtrCanFail(sizeof(DirEntry),"DirEntry");
  287.                                 if (New == NIL)
  288.                                     {
  289.                                         /* this operation is legal because the directory structure is */
  290.                                         /* always consistent at all times. */
  291.                                      DeathPoint:
  292.                                         DisposeDirectory(Dir);
  293.                                         APRINT(("-ReadDirectory failed"));
  294.                                         return NIL;
  295.                                     }
  296.                                 Where = (FSSpec*)AllocPtrCanFail(sizeof(FSSpec),"FSSpec");
  297.                                 if (Where == NIL)
  298.                                     {
  299.                                      AnotherWhereFailurePoint:
  300.                                         ReleasePtr((char*)New);
  301.                                         goto DeathPoint;
  302.                                     }
  303.                                 EXECUTE(if (!Eep_RegisterFileSpec((FileSpec*)Where))
  304.                                     {ReleasePtr((char*)Where); goto AnotherWhereFailurePoint;})
  305.                                 FSMakeFSSpec(Params.volumeParam.ioVRefNum,0/*don'tknowroot*/,
  306.                                     DiskName,Where);
  307.                                 New->WhereIsIt = (FileSpec*)Where;
  308.                                 New->ItemType = eDirectory;
  309.                                 New->Next = NIL;
  310.                                 if (Tail != NIL)
  311.                                     {
  312.                                         Tail->Next = New;
  313.                                     }
  314.                                  else
  315.                                     {
  316.                                         Dir->List = New;
  317.                                     }
  318.                                 Tail = New;
  319.                             }
  320.                          else
  321.                             {
  322.                                 /* error -- no more volumes mounted.  We is done */
  323.                                 EnergizerBunny = False;
  324.                             }
  325.                     }
  326.             }
  327.         return Dir;
  328.         APRINT(("-ReadDirectory %xl",Dir));
  329.     }
  330.  
  331.  
  332. /* get rid of the directory structure when we are done with it */
  333. void                            DisposeDirectory(DirectoryRec* Dir)
  334.     {
  335.         CheckPtrExistence(Dir);
  336.         while (Dir->List != NIL)
  337.             {
  338.                 DirEntry*            Temp;
  339.  
  340.                 Temp = Dir->List;
  341.                 Dir->List = Dir->List->Next;
  342.                 DisposeFileSpec(Temp->WhereIsIt);
  343.                 ReleasePtr((char*)Temp);
  344.             }
  345.         ReleasePtr((char*)Dir);
  346.     }
  347.  
  348.  
  349. /* find out how many entries there are in the directory structure */
  350. long                            GetDirectorySize(DirectoryRec* Dir)
  351.     {
  352.         DirEntry*                Scan;
  353.         long                        Count;
  354.  
  355.         CheckPtrExistence(Dir);
  356.         Count = 0;
  357.         Scan = Dir->List;
  358.         while (Scan != NIL)
  359.             {
  360.                 Count += 1;
  361.                 Scan = Scan->Next;
  362.             }
  363.         return Count;
  364.     }
  365.  
  366.  
  367. /* return the item type of an indexed directory entry (indices start from 0) */
  368. /* Indices start from 0 up to GetDirectorySize() - 1 */
  369. DirFileTypes            GetDirectoryEntryType(DirectoryRec* Dir, long Index)
  370.     {
  371.         DirEntry*                Scan;
  372.         long                        Count;
  373.  
  374.         CheckPtrExistence(Dir);
  375.         Count = 0;
  376.         Scan = Dir->List;
  377.         while (Scan != NIL)
  378.             {
  379.                 if (Count == Index)
  380.                     {
  381.                         return Scan->ItemType;
  382.                     }
  383.                 Count += 1;
  384.                 Scan = Scan->Next;
  385.             }
  386.         EXECUTE(PRERR(ForceAbort,"GetDirectoryEntryType:  Index is out of range"));
  387.     }
  388.  
  389.  
  390. /* return a handle containing the name of the specified directory entry or NIL */
  391. /* if allocation failed */
  392. char*                            GetDirectoryEntryName(DirectoryRec* Dir, long Index)
  393.     {
  394.         DirEntry*                Scan;
  395.         long                        Count;
  396.  
  397.         CheckPtrExistence(Dir);
  398.         Count = 0;
  399.         Scan = Dir->List;
  400.         while (Scan != NIL)
  401.             {
  402.                 if (Count == Index)
  403.                     {
  404.                         char*                        NamePtr;
  405.  
  406.                         NamePtr = ExtractFileName(Scan->WhereIsIt);
  407.                         if (NamePtr == NIL)
  408.                             {
  409.                                 return NIL;
  410.                             }
  411.                         return NamePtr;
  412.                     }
  413.                 Count += 1;
  414.                 Scan = Scan->Next;
  415.             }
  416.         EXECUTE(PRERR(ForceAbort,"GetDirectoryEntryName:  Index is out of range"));
  417.     }
  418.  
  419.  
  420. /* get a file spec describing a directory entry */
  421. /* this entry is a standard FileSpec, the same type as used in the Files module */
  422. /* and should be manipulated and disposed using routines from Files */
  423. FileSpec*                    GetDirectoryEntryFileSpec(DirectoryRec* Dir, long Index)
  424.     {
  425.         DirEntry*                Scan;
  426.         long                        Count;
  427.  
  428.         CheckPtrExistence(Dir);
  429.         Count = 0;
  430.         Scan = Dir->List;
  431.         while (Scan != NIL)
  432.             {
  433.                 if (Count == Index)
  434.                     {
  435.                         return DuplicateFileSpec(Scan->WhereIsIt);
  436.                     }
  437.                 Count += 1;
  438.                 Scan = Scan->Next;
  439.             }
  440.         EXECUTE(PRERR(ForceAbort,"GetDirectoryEntryFileSpec:  Index is out of range"));
  441.     }
  442.  
  443.  
  444. typedef enum {eLessThan EXECUTE(= -4521), eEqualTo, eGreaterThan} SortTypes;
  445.  
  446. /* local routine for comparing the directories */
  447. static SortTypes    NoCaseSort2(char* Left, char* Right, long MaxCount)
  448.     {
  449.         unsigned char        LeftTemp;
  450.         unsigned char        RightTemp;
  451.  
  452.      LoopPoint:
  453.         if (((*Left == 0) && (*Right == 0)) || (MaxCount == 0))
  454.             {
  455.                 return eEqualTo;
  456.             }
  457.         LeftTemp = *Left;
  458.         if ((LeftTemp >= 'A') && (LeftTemp <= 'Z'))
  459.             {
  460.                 LeftTemp = LeftTemp - 'A' + 'a';
  461.             }
  462.         RightTemp = *Right;
  463.         if ((RightTemp >= 'A') && (RightTemp <= 'Z'))
  464.             {
  465.                 RightTemp = RightTemp - 'A' + 'a';
  466.             }
  467.         if (LeftTemp < RightTemp)
  468.             {
  469.                 return eLessThan;
  470.             }
  471.         if (LeftTemp > RightTemp)
  472.             {
  473.                 return eGreaterThan;
  474.             }
  475.         Left += 1;
  476.         Right += 1;
  477.         MaxCount -= 1;
  478.         goto LoopPoint;
  479.     }
  480.  
  481. static SortTypes    NoCaseSort(char* Left, char* Right)
  482.     {
  483.         unsigned char        LeftTemp;
  484.         unsigned char        RightTemp;
  485.         long                        MaxCount;
  486.  
  487.         MaxCount = PtrSize(Left);
  488.         if (PtrSize(Right) < MaxCount)
  489.             {
  490.                 MaxCount = PtrSize(Right);
  491.             }
  492.      LoopPoint:
  493.         if (((*Left == 0) && (*Right == 0)) || (MaxCount == 0))
  494.             {
  495.                 return eEqualTo;
  496.             }
  497.         LeftTemp = *Left;
  498.         if ((LeftTemp >= 'A') && (LeftTemp <= 'Z'))
  499.             {
  500.                 LeftTemp = LeftTemp - 'A' + 'a';
  501.             }
  502.         RightTemp = *Right;
  503.         if ((RightTemp >= 'A') && (RightTemp <= 'Z'))
  504.             {
  505.                 RightTemp = RightTemp - 'A' + 'a';
  506.             }
  507.         if (LeftTemp < RightTemp)
  508.             {
  509.                 return eLessThan;
  510.             }
  511.         if (LeftTemp > RightTemp)
  512.             {
  513.                 return eGreaterThan;
  514.             }
  515.         Left += 1;
  516.         Right += 1;
  517.         MaxCount -= 1;
  518.         goto LoopPoint;
  519.     }
  520.  
  521.  
  522. /* resort the directory alphabetically.  Returns True if it succeeded or */
  523. /* False if it failed. */
  524. MyBoolean                    ResortDirectory(DirectoryRec* Dir)
  525.     {
  526.         DirEntry*                OldThangs;
  527.  
  528.         CheckPtrExistence(Dir);
  529.         OldThangs = Dir->List;
  530.         if (OldThangs == NIL)
  531.             {
  532.                 return True; /* hey, nothing to sort */
  533.             }
  534.         Dir->List = OldThangs; /* first item doesn't sort anyway, so putting it on */
  535.         OldThangs = OldThangs->Next; /* right away simplifies things later... */
  536.         Dir->List->Next = NIL;
  537.         /* ...since we can always assume Dir->List != NIL */
  538.         while (OldThangs != NIL) /* yup, a good olde n^2 insertion sort */
  539.             {
  540.                 DirEntry*                OurItem;
  541.                 DirEntry*                DirScan;
  542.                 DirEntry*                DirLag;
  543.                 char*                        ItemName;
  544.                 char*                        ScanName;
  545.  
  546.                 OurItem = OldThangs;
  547.                 OldThangs = OldThangs->Next; /* advance before we destroy .Next */
  548.                 ItemName = ExtractFileName(OurItem->WhereIsIt);
  549.                 if (ItemName == NIL)
  550.                     {
  551.                      FailurePoint1:
  552.                         return False;
  553.                     }
  554.                 ERROR(PtrSize(ItemName) == 0,PRERR(AllowResume,
  555.                     "ResortDirectory:  An item's FSSpec has an empty name"));
  556.                 DirLag = NIL;
  557.                 DirScan = Dir->List;
  558.                 while (DirScan != NIL)
  559.                     {
  560.                         SortTypes                    CompareResult;
  561.  
  562.                         ScanName = ExtractFileName(DirScan->WhereIsIt);
  563.                         if (ScanName == NIL)
  564.                             {
  565.                                 ReleasePtr(ItemName);
  566.                                 goto FailurePoint1;
  567.                             }
  568.                         ERROR(PtrSize(ScanName) == 0,PRERR(AllowResume,
  569.                             "ResortDirectory:  An item's FSSpec has an empty name"));
  570.                         CompareResult = NoCaseSort(ItemName,ScanName);
  571.                         ReleasePtr(ScanName);
  572.                         switch (CompareResult)
  573.                             {
  574.                                 case eLessThan:
  575.                                     /* our item comes first, insert */
  576.                                     OurItem->Next = DirScan;
  577.                                     if (DirLag != NIL)
  578.                                         {
  579.                                             DirLag->Next = OurItem;
  580.                                         }
  581.                                      else
  582.                                         {
  583.                                             Dir->List = OurItem;
  584.                                         }
  585.                                     goto ExitInnerLoopSortPoint;
  586.                                     break;
  587.                                 case eEqualTo:
  588.                                     /* we continue to next one so that this is a stable sort. */
  589.                                     break;
  590.                                 case eGreaterThan:
  591.                                     /* we continue to next one. */
  592.                                     break;
  593.                             }
  594.                         DirLag = DirScan;
  595.                         DirScan = DirScan->Next;
  596.                     }
  597.                 /* if we got all the way through, then tack it on the end */
  598.                 ERROR(DirLag == NIL,PRERR(ForceAbort,"ResortDirectory:  Internal error"));
  599.                 OurItem->Next = DirLag->Next; /* DirLag != NIL; see outer while comment */
  600.                 DirLag->Next = OurItem;
  601.              ExitInnerLoopSortPoint: /* exited from inner loop since we were able to insert */
  602.                 ReleasePtr(ItemName);
  603.             }
  604.     }
  605.  
  606.  
  607. /* this compares to file specifications and returns True if they refer to the */
  608. /* same file */
  609. MyBoolean                    CompareFileSpecs(FileSpec* First, FileSpec* Second)
  610.     {
  611.         CheckPtrExistence(First);
  612.         CheckPtrExistence(Second);
  613.         ValidateFileSpec(First);
  614.         ValidateFileSpec(Second);
  615.         if ((((FSSpec*)First)->vRefNum != ((FSSpec*)Second)->vRefNum)
  616.             || (((FSSpec*)First)->parID != ((FSSpec*)Second)->parID))
  617.             {
  618.                 return False;
  619.             }
  620.         if (((FSSpec*)First)->name[0] != ((FSSpec*)Second)->name[0])
  621.             {
  622.                 return False;
  623.             }
  624.         /* remember to use a case sensitive compare for UNIX! */
  625.         return (eEqualTo == NoCaseSort2((char*)&(((FSSpec*)First)->name[1]),
  626.             (char*)&(((FSSpec*)Second)->name[1]),((FSSpec*)First)->name[0]));
  627.     }
  628.  
  629.  
  630. /* dereference a symbolic link one level only */
  631. FileSpec*                    DereferenceSymbolicLink(FileSpec* Source)
  632.     {
  633.         FSSpec*                    New;
  634.         OSErr                        Error;
  635.         Boolean                    TargetWasFolder;
  636.         Boolean                    TargetWasAlias;
  637.  
  638.         APRINT(("+DereferenceSymbolicLink %r",Source));
  639.         CheckPtrExistence(Source);
  640.         ValidateFileSpec(Source);
  641.         New = (FSSpec*)AllocPtrCanFail(sizeof(FSSpec),"FSSpec");
  642.         *New = *(FSSpec*)Source;
  643.         if (New == NIL)
  644.             {
  645.                 APRINT(("-DereferenceSymbolicLink Failed"));
  646.                 return NIL;
  647.             }
  648.         AUDITFILESPEC(*New);
  649.         Error = ResolveAliasFile(New,False/*onelevel*/,&TargetWasFolder,&TargetWasAlias);
  650.         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",(short)Error,
  651.             (short)TargetWasFolder,(short)TargetWasAlias));
  652.         if (Error == noErr)
  653.             {
  654.                 if (TargetWasAlias)
  655.                     {
  656.                         EXECUTE(if (!Eep_RegisterFileSpec((FileSpec*)New))
  657.                             {ReleasePtr((char*)New); New = NIL;})
  658.                         /* it was an alias */
  659.                         APRINT(("-DereferenceSymbolicLink %r",New));
  660.                         return (FileSpec*)New;
  661.                     }
  662.             }
  663.         /* wasn't an alias, so we return NIL */
  664.         ReleasePtr((char*)New); /* hasn't been registered yet */
  665.         APRINT(("-DereferenceSymbolicLink NIL"));
  666.         return NIL;
  667.     }
  668.  
  669.  
  670. /* get root file specification.  On UNIX, this would return "/"; on Macintosh, */
  671. /* it returns a bogus file descriptor */
  672. FileSpec*                    GetRootFileSpec(void)
  673.     {
  674.         FSSpec*                        Thang;
  675.  
  676.         Thang = (FSSpec*)AllocPtrCanFail(sizeof(FSSpec),"RootFileSpec");
  677.         if (Thang != NIL)
  678.             {
  679.                 Thang->vRefNum = 0;
  680.                 Thang->parID = 0;
  681.                 Thang->name[0] = 0;
  682.                 if (!Eep_RegisterFileSpec((FileSpec*)Thang))
  683.                     {
  684.                         ReleasePtr((char*)Thang);
  685.                         Thang = NIL;
  686.                     }
  687.             }
  688.         return (FileSpec*)Thang;
  689.     }
  690.  
  691.  
  692. /* get statistics for a file */
  693. MyBoolean                    GetFileStatistics(struct FileSpec* File, FileInfoRec* InfoOut)
  694.     {
  695.         FInfo                        FinderInfo;
  696.         CInfoPBRec            InfoPB;
  697.         OSErr                        Error;
  698.         DateTimeRec            When;
  699.  
  700.         CheckPtrExistence(File);
  701.         ValidateFileSpec(File);
  702.         FSpGetFInfo((FSSpec*)File,&FinderInfo);
  703.         InfoOut->CreatorCode = FinderInfo.fdCreator;
  704.         InfoOut->FileTypeCode = FinderInfo.fdType;
  705.         InfoPB.hFileInfo.ioVRefNum = ((FSSpec*)File)->vRefNum;
  706.         InfoPB.hFileInfo.ioNamePtr = ((FSSpec*)File)->name;
  707.         InfoPB.hFileInfo.ioDirID = ((FSSpec*)File)->parID;
  708.         InfoPB.hFileInfo.ioFDirIndex = 0;
  709.         Error = PBGetCatInfo(&InfoPB,False);
  710.         if (Error != noErr)
  711.             {
  712.                 return False;
  713.             }
  714.         Secs2Date(InfoPB.hFileInfo.ioFlCrDat,&When);
  715.         InfoOut->CreationDate.Year = When.year;
  716.         InfoOut->CreationDate.Month = When.month - 1;
  717.         InfoOut->CreationDate.Day = When.day - 1;
  718.         InfoOut->CreationDate.Hour = When.hour;
  719.         InfoOut->CreationDate.Minute = When.minute;
  720.         InfoOut->CreationDate.Second = When.second;
  721.         InfoOut->CreationDate.DayOfTheWeek = When.dayOfWeek - 1;
  722.         Secs2Date(InfoPB.hFileInfo.ioFlMdDat,&When);
  723.         InfoOut->LastModificationDate.Year = When.year;
  724.         InfoOut->LastModificationDate.Month = When.month - 1;
  725.         InfoOut->LastModificationDate.Day = When.day - 1;
  726.         InfoOut->LastModificationDate.Hour = When.hour;
  727.         InfoOut->LastModificationDate.Minute = When.minute;
  728.         InfoOut->LastModificationDate.Second = When.second;
  729.         InfoOut->LastModificationDate.DayOfTheWeek = When.dayOfWeek - 1;
  730.         return True;
  731.     }
  732.  
  733.  
  734. /* create a new directory with the specified file specification. */
  735. MyBoolean                    CreateNewDirectory(struct FileSpec* DirLocation)
  736.     {
  737.         long                        NewDirID;
  738.  
  739.         CheckPtrExistence(DirLocation);
  740.         ValidateFileSpec(DirLocation);
  741.         return (noErr == FSpDirCreate((FSSpec*)DirLocation,smSystemScript,&NewDirID));
  742.     }
  743.  
  744.  
  745. /* find out of the specified file specification is a directory */
  746. MyBoolean                    IsTheFileSpecADirectory(struct FileSpec* Spec)
  747.     {
  748.         OSErr                    Error;
  749.         Boolean                TargetWasFolder;
  750.         Boolean                TargetWasAlias;
  751.         FSSpec                Copy;
  752.  
  753.         APRINT(("+IsTheFileSpecADirectory %r",Spec));
  754.         CheckPtrExistence(Spec);
  755.         ValidateFileSpec(Spec);
  756.         Copy = *(FSSpec*)Spec;
  757.         AUDITFILESPEC(Copy);
  758.         Error = ResolveAliasFile(&Copy,True/*all of them*/,&TargetWasFolder,&TargetWasAlias);
  759.         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",(short)Error,
  760.             (short)TargetWasFolder,(short)TargetWasAlias));
  761.         if (Error == fnfErr)
  762.             {
  763.                 /* assume it's a volume. */
  764.                 APRINT(("-IsTheFileSpecADirectory True (assumed volume)"));
  765.                 return True;
  766.             }
  767.         APRINT(("-IsTheFileSpecADirectory %b",(short)TargetWasFolder));
  768.         return TargetWasFolder;
  769.     }
  770.  
  771.  
  772. /* find out if the specified file is a symbolic link */
  773. MyBoolean                    IsTheFileSpecASymbolicLink(struct FileSpec* Spec)
  774.     {
  775.         OSErr                    Error;
  776.         Boolean                TargetWasFolder;
  777.         Boolean                TargetWasAlias;
  778.         FSSpec                Copy;
  779.  
  780.         APRINT(("+IsTheFileSpecASymbolicLink %r",Spec));
  781.         CheckPtrExistence(Spec);
  782.         ValidateFileSpec(Spec);
  783.         Copy = *(FSSpec*)Spec;
  784.         AUDITFILESPEC(Copy);
  785.         Error = ResolveAliasFile(&Copy,False/*one of them*/,&TargetWasFolder,&TargetWasAlias);
  786.         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",(short)Error,
  787.             (short)TargetWasFolder,(short)TargetWasAlias));
  788.         if (Error == fnfErr)
  789.             {
  790.                 /* assume it's a volume. */
  791.                 APRINT(("-IsTheFileSpecASymbolicLink False (assumed volume)"));
  792.                 return False;
  793.             }
  794.         APRINT(("-IsTheFileSpecASymbolicLink %b",(short)TargetWasAlias));
  795.         return TargetWasAlias;
  796.     }
  797.  
  798.  
  799. /* obtain a file spec for a file inside of the specified directory.  the file */
  800. /* name must be a non-null-terminated heap block */
  801. struct FileSpec*    FileSpecForFileInDirectory(struct FileSpec* DirLocation,
  802.                                         char* Filename)
  803.     {
  804.         FSSpec*                    Result;
  805.         OSErr                        Error;
  806.         Boolean                    TargetWasFolder;
  807.         Boolean                    TargetWasAlias;
  808.         FSSpec                    Copy;
  809.         long                        DirID;
  810.         Str63                        Name;
  811.         long                        Scan;
  812.         long                        Limit;
  813.  
  814.         APRINT(("+FileSpecForFileInDirectory Dir=%r, Name=%r",DirLocation,Filename));
  815.  
  816.         CheckPtrExistence(DirLocation);
  817.         ValidateFileSpec(DirLocation);
  818.         CheckPtrExistence(Filename);
  819.  
  820.         Result = (FSSpec*)AllocPtrCanFail(sizeof(FSSpec),"FileSpecForFileInDirectory FSSpec");
  821.         if (Result == NIL)
  822.             {
  823.              FailurePoint1:
  824.                 APRINT(("-FileSpecForFileInDirectory failed"));
  825.                 return NIL;
  826.             }
  827.         Copy = *(FSSpec*)DirLocation;
  828.         AUDITFILESPEC(Copy);
  829.         Error = ResolveAliasFile(&Copy,True/*all of them*/,&TargetWasFolder,&TargetWasAlias);
  830.         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",(short)Error,
  831.             (short)TargetWasFolder,(short)TargetWasAlias));
  832.         if (Error == fnfErr)
  833.             {
  834.                 /* assume it's a volume. */
  835.              FailurePoint2:
  836.                 ReleasePtr((char*)Result);
  837.                 goto FailurePoint1;
  838.             }
  839.         if (!TargetWasFolder)
  840.             {
  841.              FailurePoint3:
  842.                 goto FailurePoint2;
  843.             }
  844.         if (noErr != FDerefFolder(&Copy,&DirID))
  845.             {
  846.              FailurePoint4:
  847.                 goto FailurePoint3;
  848.             }
  849.         Limit = PtrSize(Filename);
  850.         if (Limit > 31)
  851.             {
  852.                 Limit = 31;
  853.             }
  854.         Name[0] = Limit;
  855.         for (Scan = 0; Scan < Limit; Scan += 1)
  856.             {
  857.                 Name[1 + Scan] = Filename[Scan];
  858.             }
  859.         Error = FSMakeFSSpec(((FSSpec*)DirLocation)->vRefNum,DirID,Name,Result);
  860.         APRINT((" ResolveAliasFile:  FSMakeFSSpec returned %s",(short)Error));
  861.         if ((noErr != Error) && (fnfErr != Error))
  862.             {
  863.                 /* file not found is natural, since they're probably creating it. */
  864.              FailurePoint5:
  865.                 goto FailurePoint4;
  866.             }
  867.         if (!Eep_RegisterFileSpec((FileSpec*)Result))
  868.             {
  869.              FailurePoint6:
  870.                 goto FailurePoint5;
  871.             }
  872.  
  873.         APRINT(("-FileSpecForFileInDirectory %r",Result));
  874.         return (FileSpec*)Result;
  875.     }
  876.